//
//  ILGBaseModel.m
//  ILGDemo
//
//  Created by HappyWork on 2018/11/8.
//  Copyright © 2018 Happy. All rights reserved.
//

#import "ILGBaseModel.h"

@interface ILGBaseModel () <NSCoding, NSCopying, NSMutableCopying>
@property (nonatomic, strong) NSMutableDictionary *tmpDict;
@end

@implementation ILGBaseModel

#pragma mark - DictionaryToModel

+ (instancetype)modelWithDictionary:(NSDictionary *)dict {
    ILGBaseModel *model = [[self alloc] init];
    [model setValuesForKeysWithDictionary:dict];
    return model;
}

- (void)setValue:(id)value forKey:(NSString *)key {
    if ([[self keysForNumberValueNeedingTanslateToString] containsObject:key]) {
        value = [NSString stringWithFormat:@"%@", value];
    }
    [super setValue:value forKey:key];
}

// 在调用addObserver:forkeyPath:options:context:方法时，就会走到这个方法里面，当对应的keyPath 进到这个方法时，返回NO,那就是关闭自动KVO；同时注意，除此之外的所有keyPath 都让它走父类的方法，这样就不会影响其他的KVO。
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
    //判断对应的键值
    if ([key isEqualToString:@"name"]) {
        return NO;
    }
    return [super automaticallyNotifiesObserversForKey:key];
}

//- (id)valueForUndefinedKey:(NSString *)key {
//    NSLog(@"没找到呀");
//    return [NSNull null];
//}
//
//- (void)setValue:(id)value forUndefinedKey:(NSString *)key {
//    NSString *tmpKey = [[self keysForReplacingOldKeys] objectForKey:key];
//    if (tmpKey) {
//        [self setValue:value forKey:tmpKey];
//        return;
//    }
//    
//    if (!_tmpDict) {
//        _tmpDict = [NSMutableDictionary dictionary];
//    }
//    
//    [_tmpDict setObject:value forKey:key];
//}


- (NSDictionary *)undefinedDict {
    return _tmpDict;
}

- (NSArray *)keysForNumberValueNeedingTanslateToString {
    return @[];
}

- (NSDictionary *)keysForReplacingOldKeys {
    return @{};
}


#pragma mark - NSCoding
// 可以归档父类里的所有属性，但不能归档父类的私有实例变量
- (id)initWithCoder:(NSCoder *)aDecoder {
    self = [self init];
    if (!self) {
        return nil;
    }
    
    Class cls = [self class];
    while (cls != [NSObject class]) {
        /*判断是自身类还是父类*/
        BOOL bIsSelfClass = (cls == [self class]);
        unsigned int iVarCount = 0;
        unsigned int propVarCount = 0;
        unsigned int sharedVarCount = 0;
        Ivar *ivarList = bIsSelfClass ? class_copyIvarList([cls class], &iVarCount) : NULL;/*变量列表，含属性以及私有变量*/
        objc_property_t *propList = bIsSelfClass ? NULL : class_copyPropertyList(cls, &propVarCount);/*属性列表*/
        sharedVarCount = bIsSelfClass ? iVarCount : propVarCount;
            
        for (int i = 0; i < sharedVarCount; i++) {
            const char *varName = bIsSelfClass ? ivar_getName(*(ivarList + i)) : property_getName(*(propList + i));
            NSString *key = [NSString stringWithUTF8String:varName];
            id varValue = [aDecoder decodeObjectForKey:key];
            NSArray *filters = @[@"superclass", @"description", @"debugDescription", @"hash"];
            if (varValue && [filters containsObject:key] == NO) {
                [self setValue:varValue forKey:key];
            }
        }
        free(ivarList);
        free(propList);
        cls = class_getSuperclass(cls);
    }
    return self;
}

- (void)encodeWithCoder:(NSCoder *)aCoder {
    Class cls = [self class];
    while (cls != [NSObject class]) {
        /*判断是自身类还是父类*/
        BOOL bIsSelfClass = (cls == [self class]);
        unsigned int iVarCount = 0;
        unsigned int propVarCount = 0;
        unsigned int sharedVarCount = 0;
        Ivar *ivarList = bIsSelfClass ? class_copyIvarList([cls class], &iVarCount) : NULL;/*变量列表，含属性以及私有变量*/
        objc_property_t *propList = bIsSelfClass ? NULL : class_copyPropertyList(cls, &propVarCount);/*属性列表*/
        sharedVarCount = bIsSelfClass ? iVarCount : propVarCount;
        
        for (int i = 0; i < sharedVarCount; i++) {
            const char *varName = bIsSelfClass ? ivar_getName(*(ivarList + i)) : property_getName(*(propList + i));
            NSString *key = [NSString stringWithUTF8String:varName];
            /*valueForKey只能获取本类所有变量以及所有层级父类的属性，不包含任何父类的私有变量(会崩溃)*/
            id varValue = [self valueForKey:key];
            NSArray *filters = @[@"superclass", @"description", @"debugDescription", @"hash"];
            if (varValue && [filters containsObject:key] == NO) {
                [aCoder encodeObject:varValue forKey:key];
            }
        }
        free(ivarList);
        free(propList);
        cls = class_getSuperclass(cls);
    }
}


#pragma mark - Copy

- (id)copyWithZone:(NSZone *)zone {
    return self;
}

- (id)mutableCopyWithZone:(NSZone *)zone {
    id model = [[[self class] allocWithZone:zone] init];

    Class cls = [self class];
    while (cls != [NSObject class]) {
        /*判断是自身类还是父类*/
        BOOL bIsSelfClass = (cls == [self class]);
        unsigned int iVarCount = 0;
        unsigned int propVarCount = 0;
        unsigned int sharedVarCount = 0;
        Ivar *ivarList = bIsSelfClass ? class_copyIvarList([cls class], &iVarCount) : NULL;/*变量列表，含属性以及私有变量*/
        objc_property_t *propList = bIsSelfClass ? NULL : class_copyPropertyList(cls, &propVarCount);/*属性列表*/
        sharedVarCount = bIsSelfClass ? iVarCount : propVarCount;

        for (int i = 0; i < sharedVarCount; i++) {
            const char *varName = bIsSelfClass ? ivar_getName(*(ivarList + i)) : property_getName(*(propList + i));
            NSString *key = [NSString stringWithUTF8String:varName];
            NSArray *filters = @[@"superclass", @"description", @"debugDescription", @"hash"];
            id varValue = [self valueForKey:key];
            if (varValue && [filters containsObject:key] == NO) {
                [model setValue:varValue forKey:key];
            }
        }
        free(ivarList);
        free(propList);
        cls = class_getSuperclass(cls);
    }

    return model;
}


#pragma mark - others

+ (void)printIvarNamesAndTypes
{
    unsigned int count = 0;
    // 成员变量数组 指向数组第0个元素
    Ivar *ivarList = class_copyIvarList([self class], &count);
    
    // 遍历所有成员变量
    for (int i = 0; i < count; i++) {
        
        // 获取成员变量 user
        Ivar ivar = ivarList[i];
        // 获取成员变量名称，即将C语言的字符转为OC字符串
        NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)];
        
        // 获取成员变量类型，用于获取二级字典的模型名字
        NSString *type = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
        
        NSLog(@"ivarName = %@, type = %@", ivarName, type);
    }
}

@end
